#!/usr/bin/env bash
#  Author: Mirko van der Waal, Nathan Isom
#  Mail: <mvdw at airmail dot cc>
#  Mail: <nathanisom27 at gmail.com dot cc>
#  Distributed under terms of the MIT license.
#
#  The MIT License (MIT)
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to deal
#  in the Software without restriction, including without limitation the rights
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#  copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in all
#  copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#  SOFTWARE.

# notes on differences with captain
# takes 2 args, $conf and $geom

# Obviously we set -e to quit at any error.
set -e

# auto-export variables
set -a

# Change the current directory to the lemonade directory always.
cd $(dirname $([ -L $0  ] && readlink -f $0 || echo $0))

lemonadedir="$HOME/.wm/panel"
ident="$(bspc query -M -m "$mon" --names)"

mkdir -p $lemonadedir $lemonadedir/logs $lemonadedir/lemons
[ -f "$lemonadedir/logs/$ident.log" ] && rm "$lemonadedir/logs/$ident.log"
exec 3<> "$lemonadedir/logs/$ident.log"

# helper
get_conf() {
    eval "echo \"\${${*}}\""
}

# Create the scripts directory.
lemons=$lemonadedir/lemons
juicer=$lemonadedir/juicers/$1

# The only bit of python in the code, we parse .dosini file with it.
source <(python3 -c "
import sys, re
from configparser import ConfigParser

def variable(*fmt):
    return '{}_{}="{}"'.format(*fmt)

def array(*fmt):
    return '{}_{}=({})'.format(*fmt)

# strict to false means you can have duplicate keys that override previous values.
parser = ConfigParser(strict=False)
parser.read(sys.argv[1])

for section in parser:
    for key in parser[section]:
        # Split on spaces and newlines to determine when to create arrays.
        # note: this means '^' can't be used in conf file.
        current = re.sub('[\n ]', '^', parser[section][key]).split('^')
        # Replace dashes with underscores for a better user experience.
        key = key.replace('-', '_')
        if len(current) == 1:
            print(variable(section, key, current[0]))
        else:
            print(array(section, key, ' '.join(current)))
" $juicer 2>&3) 2>&3

# "It is easier to port a shell than a shell script."
#   -- Larry Wall

process=/tmp/lemonade-process-$ident

# Always clean up when you are done.
trap "error; rm -f $process; trap - TERM; kill 0" INT ERR TERM QUIT

# Retrieve a value from the stack, the stack is a collection of all scripts with
# unique sets of characters that are used for event handles.

# Catch the strings send to stdout and execute the matching command.
handle() {
    {
        while read -r line; do
            line="$(echo $line | sed 's|"|\\"|g')"
            echo "click event: $line"
            sh -c "eval \"$line\"" &
        done
    } 2>&3
}

# Dynamically format all the available options and display them accordingly.
format() {
    {
        init=$1
        ramfile="/dev/shm/lemonade-$ident"
        ! $init && . "$ramfile"

        update_state() {
            temp="${ramfile}-temp"
            for lemon in $juicedLemons; do
                echo "__${lemon}='$(get_conf __${lemon})'" >> "$temp"
            done
            mv "$temp" "$ramfile"
        }

        dump_state=0
        while read -r line; do
            if [ "$line" = "refresh" ]; then
                eval echo \"$position\"
                continue
            fi

            eval "case ${line%%@*} in
                $switch
                *) ;;
            esac ; echo \"$position\""

            dump_state=$((dump_state + 1))

            # dump state every 10?
            if [ $dump_state -eq 10 ]; then
                dump_state=0
                update_state &
            fi
        done
    } 2>&3
}

# note enabled lemons.
juicedLemons="$(sed 's/:/ /g;s/|/ /g;' <<< $bar_format)"

# The defaults for the [bar] section. Most of the values are simplified and
# require some extra attention to be formatted correctly.
{
    declare -A bar=(
        [fg]="#FFFFFFFF"
        [bg]="#FF000000"
        [format]="||"
        [offset]=0

        [line]=2

        [underline]=false
        [overline]=false

        # padding inside sections
        [padding]=0

        [fonts]="monospace-9"
        [force]=true
        [class]="lemonbar"
        [clickables]=10)

    for section in "${!bar[@]}"; do
        if [[ $(get_conf \#bar_${section}) -eq 0 ]]; then
            #echo "No value for 'bar-$section' -> using ${bar[${section}]}"
            eval "bar_${section}=${bar[${section}]}"
        fi
    done
} 2>&3

# Whenever a value is not defined, set the default value for it.
{
    declare -A choices=(
        [fg]="${bar_fg}"
        [bg]="${bar_bg}"
        [overline]=false
        [underline]=false
        [line]=\#ff999999
        [activeline]=\#ffffffff
        [reload]=5)
    for name in $juicedLemons; do
        for section in "${!choices[@]}"; do
            if [[ $(get_conf \#${name}_${section}) -eq 0 ]]; then
                #echo "No value for '${name}-$section' -> using ${choices[${section}]}"
                eval "${name}_${section}=${choices[${section}]}"
            fi
        done
    done
} 2>&3

# now that all values will be set, process things:

# handle panel-side options
# note: pending https://github.com/LemonBoy/bar/issues/203
# todo: consider just making a lemonbar under/over to fake the line.
pos_start=""
$bar_underline && pos_start+="+u"
$bar_overline && pos_start+="+o"
if [ ! -z "$pos_start" ]; then
    # todo: make this a bar property
    pos_start="%{${pos_start}U#ffffffff}"
    pos_end="%{-o}%{-uU-}"
    bar_delimiter="${pos_start}${bar_delimiter}${pos_end}"
    position="%{l}$(echo $bar_format | sed "s/|/${pos_start}%{c}${pos_end}/; s/|/${pos_start}%{r}${pos_end}/; s/:/$bar_delimiter/g;")"
else
    position="%{l}$(echo $bar_format | sed "s/|/%{c}/;s/|/%{r}/;s/:/$bar_delimiter/g;")"
fi

# build the panel switch
switch=""
for script in $juicedLemons; do
    position=$(sed "s/${script}/\$__${script}/g" <<< $position)
    switch="$switch ${script}) __${script}=\${line#*@} ;;"
done

# Define what lines to draw (under & over), and what color they should be..
{
    declare -A line
    for name in $juicedLemons; do
        if [[ $(get_conf \#${name}_line) -eq 0 ]]; then
            #echo "No value for '${name}-line' -> using ${choices[fg]}"
            eval "${name}_line=${choices[fg]}"
        fi
        line["${name}"]="%{"
        for section in underline overline; do
            if [[ $(get_conf ${name}_${section}) = true ]]; then
                line["${name}"]+="+${section:0:1}"
            else
                echo "No value for '${name}-${section}' -> using false"
            fi
        done
        line["${name}"]+="U$(get_conf ${name}_line)}"
    done
} 2>&2

declare -A area events=(
    [mouse_middle]=2
    [scroll_down]=5
    [mouse_right]=3
    [mouse_left]=1
    [scroll_up]=4)

# Create event handles and add them in the area.
generate_mouse_events() {
    for name in ${1:-$juicedLemons}; do
        area["${name}_p"]=""
        area["${name}_s"]=""

        if [ -z "$2" ]; then
            events="${!events[@]}"
        else
            shift
            events="$@"
        fi

        for event in $events; do
            action="$(get_conf ${name}_${event} | sed 's/:/\\:/g')"
            if [[ ! -z "$action" ]]; then
                area["${name}_p"]+="%{A${events[$event]}:${action}:}"
                area["${name}_s"]+="%{A}"
            fi
        done
    done
}
generate_mouse_events

# Process input from a lemon, output settings
process_line() {
    if [[ "${output:0:4}" == "meta" ]]; then
        eval "${output:4}"
    else
        if [[ ! -z "$output" ]]; then
            bar_message="${bar_message}$(printf "%s" \
                                  "$(get_conf area[${name}_p])" \
                                  "$(get_conf line[${name}])" \
                                  "%{F$(get_conf ${name}_fg)}" \
                                  "%{B$(get_conf ${name}_bg)}" \
                                  "%{O$(iif $hold "$(($bar_padding / 2))" $bar_padding)}" \
                                  "$output" \
                                  "%{O$(iif $hold "$(($bar_padding / 2))" $bar_padding)}" \
                                    "$(get_conf area[${name}_s])" \
                                  "%{F-}%{B-}" \
                                  "%{-o}%{-uU-}")"
        fi
    fi

    if ! $hold; then
        if [[ ! -z "${bar_message}" ]]; then
            bar_message="$(printf "%s" \
                              "$(get_conf ${name}_prefix)" \
                              "${bar_message}" \
                              "$(get_conf ${name}_suffix)")"
            printf "%s" \
                   "${name}@${bar_message}" \
                   $'\n' > "$process"

            bar_message=
        fi
    fi
}

# Parse our previously collected elements to a full string.
{
    [[ -e "$process" ]] && rm "$process"
    mkfifo "$process"

    for name in $juicedLemons; do
        export juicedLemon="$name";
        if [[ ! -z $(get_conf ${name}_reload) ]]; then
            if [[ ! -z $(tr -d [:digit:]. <<< $(get_conf ${name}_reload)) ]]; then
                hold=false;
                bar_message=

                while IFS=$'\n' read -r output; do
                    process_line
                done < <(eval "$(get_conf ${name}_reload)" | "$lemons/$name") &
            else
                while true; do
                    hold=false;
                    bar_message=
                    while IFS=$'\n' read -r output; do
                        process_line
                    done < <(eval "$lemons/$name")

                    sleep $(get_conf ${name}_reload)
                done > "$process" &
            fi
        fi
    done
} 2>&3

get_geometry() {
    # mon dim
    dim() {
	      bspc query -T -m $mon | jq .rectangle.$1
    }

    gapped=$(iif "[ ! $(bspc config window_gap) -le 0 ]")
    p_gap=$(iif $gapped $p_gap 0)
    top=$(iif "[ "$p_position" = "top" ]")

	  echo "$(( $(dim width)-(p_gap*2) ))x$p_height+$(( $(dim x)+p_gap ))+$(iif $top $p_gap $(($(dim height)-(p_gap+p_height))) )"
}

# Start lemonade
launch_lemonbar() {
    set +e

    if ps -p $(echo $active_pids | sed 's/ .*//'); then
        for pid in $active_pids; do
            echo pkill $pid
            pkill $pid
            pkill lemonbar
        done
    fi

    cat "$process" | format $init | eval lemonbar \
                                   $(printf " -f \"%s\"" "${bar_fonts[@]}") \
                                   $([[ $bar_force = true ]] && echo "-d") \
                                   -u $bar_line \
                                   -a $bar_clickables \
                                   -o $bar_offset \
                                   -n "$bar_class" \
                                   -g "$(get_geometry)" \
                                   -F "\\$bar_fg" \
                                   -B "\\$bar_bg" \
                                   | handle &

    active_pids=$(jobs -l | tail -n 4 | grep -Eo ' [0-9]+')
    echo refresh > $process &

    trap launch_lemonbar USR1
    init=false
    wait
}

init=true
launch_lemonbar
